package com.egg.mall.service.sku.order.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.egg.common.core.exception.CustomException;
import com.egg.common.mybatis.service.impl.BaseServiceImpl;
import com.egg.mall.entity.sku.orders.SkuOrdersLinkSkuSnapshot;
import com.egg.mall.mapper.sku.order.SkuOrdersLinkSkuSnapshotMapper;

import com.egg.mall.entity.sku.Sku;
import com.egg.mall.entity.sku.SkuSnapshot;
import com.egg.mall.model.request.OrderPostOrPut;
import com.egg.mall.model.request.SkuStockQuantityPut;
import com.egg.mall.service.sku.order.ISkuOrdersLinkSkuSnapshotService;
import com.egg.mall.service.sku.ISkuService;
import com.egg.mall.service.sku.ISkuSnapshotService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author east
 * @since 2022-08-27
 */
@Slf4j
@Service
public class SkuOrdersLinkSkuSnapshotServiceImpl extends BaseServiceImpl<SkuOrdersLinkSkuSnapshotMapper, SkuOrdersLinkSkuSnapshot> implements ISkuOrdersLinkSkuSnapshotService {


    @Autowired
    private ISkuSnapshotService skuSnapshotService;
    @Autowired
    private ISkuService skuService;

    /**
     * sku 转为 orderSku
     *
     * @param sku
     * @param quantity
     * @return orderSku
     */
    @Override
    public SkuOrdersLinkSkuSnapshot convertOrderSku(Sku sku, Integer quantity) {
        SkuSnapshot skuSnapshot = skuSnapshotService.get(sku.getId(), sku.getSnapshotVersion());
        SkuOrdersLinkSkuSnapshot skuOrdersLinkSkuSnapshot = new SkuOrdersLinkSkuSnapshot();
        skuOrdersLinkSkuSnapshot.setQuantity(quantity);
        skuOrdersLinkSkuSnapshot.setSkuSnapshotId(skuSnapshot.getId());
        return skuOrdersLinkSkuSnapshot;
    }

    /**
     * 构建订单明细
     * -- 修改库存
     *
     * @param sourcePayAmount 应付金额(分)
     * @param sourceList
     * @return
     */
    @Override
    public List<SkuOrdersLinkSkuSnapshot> buildOrderSku(long sourcePayAmount, List<OrderPostOrPut.Sku> sourceList) {
        if (ObjectUtil.isEmpty(sourceList)) {
            return Collections.emptyList();
        }
        List<Sku> skuList = skuService.baseFindById(sourceList.stream().map(o -> o.getSkuId()).collect(Collectors.toSet()));
        // 按照skuId分组, 修正多个skuId
        Map<Long, Integer> idQuantity = sourceList.stream()
                .collect(Collectors.groupingBy(o -> o.getSkuId(), Collectors.summingInt(o -> o.getQuantity())));
        // 计算价格是否相符
        long payAmount = 0L;
        List<SkuOrdersLinkSkuSnapshot> skuOrdersLinkSkuSnapshotList = new ArrayList<>();
        // sku修改库存
        List<SkuStockQuantityPut> skuStockQuantityPutList = new ArrayList<>();
        for (Sku sku : skuList) {
            Integer quantity = idQuantity.get(sku.getId());
            if (null == quantity) {
                // 理论上不可能进入这个判断,因为循环的sku数据,由入参中的skuId查询而出
                throw new CustomException("下单商品数据异常");
            }
            int skuStockQuantity = sku.getStockQuantity() - quantity;
            if (skuStockQuantity < 0) {
                throw new CustomException("库存数量不足");
            }
            // 扣减库存
            SkuStockQuantityPut ssqu = new SkuStockQuantityPut();
            ssqu.setId(sku.getId());
            ssqu.setQuantity(Math.negateExact(quantity));
            skuStockQuantityPutList.add(ssqu);
            // 计算金额
            payAmount += sku.getPrice() * quantity;
            SkuOrdersLinkSkuSnapshot skuOrdersLinkSkuSnapshot = convertOrderSku(sku, quantity);
            skuOrdersLinkSkuSnapshotList.add(skuOrdersLinkSkuSnapshot);
        }
        if (payAmount != sourcePayAmount) {
            // SKU单价 * SKU下单数量 != 下单计算出的金额, 则视为价格异常
            throw new CustomException("价格存在变动,刷新后重新下单");
        }
        // 更新SKU库存
        skuService.optimisticAddStockQuantity(skuStockQuantityPutList);
        return skuOrdersLinkSkuSnapshotList;
    }

    /**
     * 订单查询SKU
     *
     * @param orderId
     */
    @Override
    public List<SkuOrdersLinkSkuSnapshot> findByOrderId(Serializable orderId) {
        if (ObjectUtil.isEmpty(orderId)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<SkuOrdersLinkSkuSnapshot> qw = buildLambdaQw();
        qw.eq(SkuOrdersLinkSkuSnapshot::getOrderId, orderId);
        return list(qw);
    }

    /**
     * 还原库存
     *
     * @param orderId
     */
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void resumeSkuStockByOrderId(Serializable orderId) {
        List<SkuOrdersLinkSkuSnapshot> skuOrdersLinkSkuSnapshotList = findByOrderId(orderId);
        if (ObjectUtil.isEmpty(skuOrdersLinkSkuSnapshotList)) {
            return;
        }
        List<SkuSnapshot> skuSnapshotList = skuSnapshotService.baseFindById(skuOrdersLinkSkuSnapshotList.stream().map(o -> o.getSkuSnapshotId()).collect(Collectors.toSet()));
        Map<Long, SkuSnapshot> skuSnapshotMap = skuSnapshotList.stream().collect(Collectors.toMap(o -> o.getSkuId(), o -> o));
        // sku修改库存
        List<SkuStockQuantityPut> skuStockQuantityPutList = new ArrayList<>();
        for (SkuOrdersLinkSkuSnapshot skuOrdersLinkSkuSnapshot : skuOrdersLinkSkuSnapshotList) {
            SkuSnapshot skuSnapshot = skuSnapshotMap.get(skuOrdersLinkSkuSnapshot.getSkuSnapshotId());
            if (null == skuSnapshot) {
                log.error("商品快照信息丢失, orderId : {} skuSnapshotId : {} 请尽快处理", skuOrdersLinkSkuSnapshot.getOrderId(), skuOrdersLinkSkuSnapshot.getSkuSnapshotId());
                throw new CustomException("系统异常,请联系客服");
            }
            SkuStockQuantityPut ssqu = new SkuStockQuantityPut();
            ssqu.setId(skuSnapshot.getSkuId());
            ssqu.setQuantity(Math.abs(skuOrdersLinkSkuSnapshot.getQuantity()));
            skuStockQuantityPutList.add(ssqu);
        }
        skuService.optimisticAddStockQuantity(skuStockQuantityPutList);
    }


}
